package helper

import (
	"fmt"
	"io"
	"os"
	"path/filepath"

	"Hypnos-Soundcore/components/config"
	"Hypnos-Soundcore/components/crontab"
	l "Hypnos-Soundcore/components/log"
	"Hypnos-Soundcore/components/logging"
	"Hypnos-Soundcore/constants"
	"encoding/json"
	"regexp"
	"time"
)

var (
	logWriter         io.WriteCloser
	apiWriter         io.WriteCloser
	commerceWriter    io.WriteCloser
	appTrackingWriter io.WriteCloser
	logToFile         bool
	PrefixLogging     = "logging"
	PrefixApi         = "api"
	PrefixCommerce    = "commerce"
	PrefixTracking    = "tracking"
)

// InitLogging init logger
func InitLogging(logConf logging.Config) {
	logToFile = logConf.Enable
	if logConf.Enable {
		var logErr error
		if len(logConf.FileName) > 0 {
			if logWriter, logErr = initWriter(logConf.FileName); logErr != nil {
				logConf.Enable = false
			}
		}
		if len(logConf.FileApi) > 0 {
			if apiWriter, logErr = initWriter(logConf.FileApi); logErr != nil {
				logConf.Enable = false
			}
		}
		if len(logConf.FileCommerce) > 0 {
			if commerceWriter, logErr = initWriter(logConf.FileCommerce); logErr != nil {
				logConf.Enable = false
			}
		}
		if len(logConf.FileTracking) > 0 {
			if appTrackingWriter, logErr = initWriter(logConf.FileTracking); logErr != nil {
				logConf.Enable = false
			}
		}
		logging.SetWriter(logToFile, logWriter, apiWriter, commerceWriter, appTrackingWriter)
	} else {
		// 若未开启日志，则将日志输出到标准输出
		var stdout io.Writer = os.Stdout
		if closer, ok := stdout.(io.WriteCloser); ok {
			logWriter = closer
			apiWriter = closer
			commerceWriter = closer
			appTrackingWriter = closer
			logging.SetWriter(logToFile, closer, closer, closer, closer)
		}
	}
}

func initWriter(baseName string) (ret io.WriteCloser, err error) {
	dir, _ := filepath.Split(baseName)
	if len(dir) > 0 {
		fileErr := os.MkdirAll(dir, os.ModePerm)
		if fileErr != nil {
			fmt.Println(fileErr)
		}
	}
	if ret, err = l.Open(baseName, CycleDuration, CycleFileSize, 0); err != nil {
		fmt.Println(err)
	}
	return
}

// GetLoggerWriter .
func GetLoggerWriter() io.WriteCloser {
	return logWriter
}

// GetApiWriter .
func GetApiWriter() io.WriteCloser {
	return apiWriter
}

// LoggingPrintln print error info
func LoggingPrintln(bytes []byte) (err error) {
	if logWriter != nil {
		if logToFile {
			_, err = logWriter.Write(append(bytes, '\n'))
		} else {
			msg := []byte(fmt.Sprintf("%s:%s\n", PrefixLogging, string(bytes)))
			_, err = logWriter.Write(msg)
		}
	}
	return
}

// ApiPrintln print error info
func ApiPrintln(bytes []byte) (err error) {
	if apiWriter != nil {
		if logToFile {
			_, err = apiWriter.Write(append(bytes, '\n'))
		} else {
			msg := []byte(fmt.Sprintf("%s:%s\n", PrefixApi, string(bytes)))
			_, err = apiWriter.Write(msg)
		}
	}
	return
}

// CommercePrintln print info
func CommercePrintln(bytes []byte) (err error) {
	if commerceWriter != nil {
		if logToFile {
			_, err = commerceWriter.Write(append(bytes, '\n'))
		} else {
			msg := []byte(fmt.Sprintf("%s:%s\n", PrefixCommerce, string(bytes)))
			_, err = commerceWriter.Write(msg)
		}
	}
	return
}

// AppTrackingPrintln  print info
func AppTrackingPrintln(bytes []byte) (err error) {
	if appTrackingWriter != nil {
		if logToFile {
			_, err = appTrackingWriter.Write(append(bytes, '\n'))
		} else {
			msg := []byte(fmt.Sprintf("%s:%s\n", PrefixTracking, string(bytes)))
			_, err = appTrackingWriter.Write(msg)
		}
	}
	return
}

// DoCronLogClear .
func DoCronLogClear(xc *config.XConfig) {
	// 删除7天前的日志文件
	if param := xc.GetCronInfo(constants.CronLogClear); param != nil {
		if len(param.Spec) > 0 && param.Expired > 0 {
			if err := crontab.GetCrontab().AddFunc(param.Spec, func() {
				names := []string{config.GetXConfig().Log.FileName, config.GetXConfig().Logging.FileName, config.GetXConfig().Logging.FileApi, config.GetXConfig().Logging.FileCommerce}
				doLogClearCheck(names, param)
			}); err != nil {
				ErrorPrintln(constants.TagHelper, err)
			}
		} else {
			WarningPrintln(constants.TagHelper, constants.ErrInvalidCrontab)
		}
	}
}

// 按正则查询目录下的文件
func getFilesByPattern(pathname, pattern string) (files []string, err error) {
	if len(pathname) == 0 || len(pattern) == 0 {
		return
	}

	re := regexp.MustCompile(pattern)
	if re == nil {
		return
	}

	walk := func(fn string, fi os.FileInfo, err error) error {
		if re.MatchString(fn) == false {
			return nil
		}
		if !fi.IsDir() {
			files = append(files, fn)
		}
		return nil
	}
	err = filepath.Walk(pathname, walk)
	return
}

// 删除7天前的日志文件
func doLogClearCheck(baseNames []string, ci *config.CronInfo) {
	DebugPrintln(constants.TagHelper, "doLogClearCheck start")
	var sum int
	expired := ci.GetExpired()
	interval := ci.GetInterval()
	if len(baseNames) > 0 && expired > 0 && interval > 0 {
		now := time.Now().AddDate(0, 0, -expired)
		for i := 0; i < interval; i++ {
			// 日志文件名并删除
			for _, name := range baseNames {
				if len(name) == 0 {
					continue
				}
				// 查找符合日期的所有日志文件
				filePattern := fmt.Sprintf("%s.%s.*", name, now.Format("20060102"))
				logFiles, _ := getFilesByPattern("./", filePattern)
				if len(logFiles) == 0 {
					continue
				}
				for _, oneFile := range logFiles {
					if err := DeleteLocalFile(oneFile); err == nil {
						sum++
					}
				}
			}
			now = now.AddDate(0, 0, -1)
		}
	}
	DebugPrintf(constants.TagHelper, "doLogClearCheck end, %d logs clear", sum)
}

func SaveLog(log interface{}) error {
	byteString, err := json.Marshal(log)
	if err != nil {
		return err
	}
	return LoggingPrintln(byteString)
}
